#include "sender.h"

#include "daemon.h"
#include "disk.h"
#include "base64/base64.h"

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QFile>
#include <QtGlobal>
#include <QDateTime>
#include <QStringList>


#include <fstream>
#include <iostream> //debug
using std::cout;
using std::endl;
using std::ifstream;
using std::ios;

namespace Avd {

Sender::Sender(Daemon* daemon, const QString &_serverUrl, const unsigned short _port) : daemon(daemon), serverUrl(QUrl(_serverUrl)), port(_port) {
	networkManager = new QNetworkAccessManager(this);
	connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SIGNAL(sigReply(QNetworkReply *)));
	this->serverUrl.setPort(port);	
}

Sender::~Sender(){
	delete networkManager;
}


/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askLogin>
 *   <session>2938701</session>
 *   <user>username</user>
 *   <password>password</password>
 *  </askLogin>
 */

void Sender::askLogin(const QString& user, const QString& password) throw(SenderError) {
	if(user.isEmpty())
		throw SenderError("void Sender::askLogin(const QString& user, const QString& password): undefined username.");

	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askLogin>";

	request += "<user>";
	request += user;
	request += "</user>";

	request += "<password>";
	request += password;
	request += "</password>";

	request += "</askLogin>";

	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askLogoff>
 *    <session>2938701</session>
 * </askLogoff>
 */

void Sender::askLogoff(const unsigned int session, const QString& username) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askLogoff(const unsigned int session, const QString& username): authentication needed.");
	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askLogoff>";

	request += "<session>" + QString::number(session) + "</session>";

	request += "<user>";
	request += username;
	request += "</user>";

	request += "</askLogoff>";

	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askDownload>
 *   <session>2938701</session>
 *   <path>/documents</path>
 *   <name>text.txt</name>
 * </askDownload>
 */

void Sender::askDownload(const unsigned int session, const QString& path,
												 const QString& name, const unsigned int part, const unsigned int totalParts) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askDownload(const unsigned int session, const QString& path, const QString& name): authentication needed.");
	if(name.isEmpty())
		throw SenderError("void Sender::askDownload(const unsigned int session, const QString& path, const QString& name): undefined file requested.");
	if(totalParts == 0)
		throw SenderError("void Sender::askDownload(const unsigned int session, const QString& path, const QString& name): 0 parts not allowed.");
	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askDownload>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<path>";
	request += path;
	request += "</path>";

	request += "<name>";
	request += name;
	request += "</name>";

	if(totalParts != 1) {
		request += "<part>";
		request += QString::number(part);
		request += "</part>";
		request += "<totalParts>";
		request += QString::number(totalParts);
		request += "</totalParts>";
	}

	request += "</askDownload>";
	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askUpload>
 *   <session>2938701</session>
 *   <path>/documents</path>
 *   <name>text.txt</name>
 *   <data>BASE64</data>
 *   <version>3</version>
 * </askUpload>
 */


void Sender::askUpload(const unsigned int session, const QString& path, const QString& name,
											 const unsigned int version) throw(FileError, SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askUpload(const unsigned int session, const QString& path, const QString& name, const unsigned int version): authentication needed.");
	if(name.isEmpty())
		throw SenderError("void Sender::askUpload(const unsigned int session, const QString& path, const QString& name, const unsigned int version): undefined file requested.");

	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askUpload>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<path>";
	request += path;
	request += "</path>";

	request += "<name>";
	request += name;
	request += "</name>";

	request += "<data>";

	QFile qfile(daemon->getDisk()->getAbsolutePath() + "/" + path + "/" + name);
	if(!qfile.open(QIODevice::ReadOnly))
		throw FileError("void Sender::askUpload(const unsigned int session, const QString& path, const QString& name, const unsigned int version): could not read file");

	ifstream::pos_type size;
	char *data;

	ifstream file (qfile.fileName().toStdString().c_str(), ios::in | ios::binary | ios::ate);
	if (file.is_open())
	{
		size = file.tellg();
		data = new char [size];
		file.seekg (0, ios::beg);
		file.read (data, size);
		file.close();
	}
	else
		throw FileError("void Sender::askUpload(const unsigned int session, const QString& path, const QString& name, const unsigned int version): fail while reading file.");

	char* dest = new char [modp_b64w_encode_len(qfile.size())];
	int len = modp_b64w_encode(dest, data, qfile.size());
	if (len == -1) {
		throw FileError("void Sender::askUpload(const unsigned int session, const QString& path, const QString& name, const unsigned int version): fail while encoding file.");
	}

	request += dest;
	request += "</data>";

	delete[] data;
	delete[] dest;

	request += "<version>";
	request += QString::number(version);
	request += "</version>";

	request += "</askUpload>";

	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askList>
 *   <session>2938701</session>
 *   <path>/documents</path>
 * </askList>
 */

void Sender::askList(const unsigned int session, const QString& path) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askList(const unsigned int session, const QString& path): authentication needed.");
	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askList>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<path>";
	request += path;
	request += "</path>";

	request += "</askList>";
	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <askMove>
 *   <session>2938701</session>
 *   <oldPath>/documents</oldPath>
 *   <oldName>text.txt</oldName>
 *   <newPath>/documents</newPath>
 *   <newName>text.txt</Newname>
 * </askMove>
 */

void Sender::askMove(const unsigned int session,
										 const QString& oldPath, const QString& oldName,
										 const QString& newPath, const QString& newName) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askMove(const unsigned int session, const QString& oldPath, const QString& oldName, const QString& newPath, const QString& newName): authentication needed.");
	if(oldName.isEmpty())
		throw SenderError("void Sender::askMove(const unsigned int session, const QString& oldPath, const QString& oldName, const QString& newPath, const QString& newName): undefined file to be moved.");
	if(newName.isEmpty())
		throw SenderError("void Sender::askMove(const unsigned int session, const QString& oldPath, const QString& oldName, const QString& newPath, const QString& newName): undefined file to copy.");

	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askMove>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<oldPath>";
	request += oldPath;
	request += "</oldPath>";

	request += "<oldName>";
	request += oldName;
	request += "</oldName>";

	request += "<newPath>";
	request += newPath;
	request += "</newPath>";

	request += "<newName>";
	request += newName;
	request += "</newName>";

	request += "</askMove>";

	send(request);
}

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 *   <askDelete>
 *     <session>15</session>
 *     <path>/documents</path>
 *     <name>text.txt</name>
 *   </askDelete>
 */

void Sender::askDelete(const unsigned int session, const QString& path,
											 const QString& name) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askDelete(const unsigned int session, const QString& path, const QString& name): authentication needed.");
	if(name.isEmpty())
		throw SenderError("void Sender::askDelete(const unsigned int session, const QString& path, const QString& name): undefined file or folder to delete.");

	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askDelete>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<path>";
	request += path;
	request += "</path>";

	request += "<name>";
	request += name;
	request += "</name>";

	request += "</askDelete>";
	send(request);
}


void Sender::askCreateFolder(const unsigned int session, const QString& path,
														 const QString& name) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askCreateFolder(const unsigned int session, const QString& path, const QString& name): authentication needed.");
	if(name.isEmpty())
		throw SenderError("void Sender::askCreateFolder(const unsigned int session, const QString& path, const QString& name): undefined folder to create.");

	QString request;
	request += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	request += "<askCreateFolder>";
	request += "<session>" + QString::number(session) + "</session>";

	request += "<path>";
	request += path;
	request += "</path>";

	request += "<name>";
	request += name;
	request += "</name>";

	request += "</askCreateFolder>";


	send(request);
}

void Sender::askCopy(const unsigned int session,
										 const QString& path, const QString& name,
										 const QString& copyPath) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askCopy(const unsigned int session, const QString& path, const QString& name, const QString& copyPath):  authentication needed.");
	if(name.isEmpty())
		throw SenderError("void Sender::askCopy(const unsigned int session, const QString& path, const QString& name, const QString& copyPath): undefined file to be copied.");
	if(copyPath.isEmpty())
		throw SenderError("void Sender::askCopy(const unsigned int session, const QString& path, const QString& name, const QString& copyPath): undefined path to copy.");

	throw SenderError("void Sender::askCopy(const unsigned int session, const QString& path, const QString& name, const QString& copyPath): not implemented.");

	//send(request);
}


void Sender::askVersion(const unsigned int session,
												const QString& path, const QString& name) throw(SenderError) {
	if(!daemon->connected())
		throw SenderError("void Sender::askVersion(const unsigned int session, const QString& path, const QString& name): authentication needed.");

	throw SenderError("void Sender::askVersion(const unsigned int session, const QString& path, const QString& name): not implemented.");

	//send(request);
}

void Sender::send(const QString &request) {
	cout << "Sending: " << request.toStdString() << endl;
	networkManager->post(QNetworkRequest(serverUrl), request.toLatin1());
}

}
